﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using System.Net;

namespace cc2530.Lib.contiki
{
    public class IPv6Address:IPAddress
    {
        /*
            Reserved for NSAP Allocation                  01xx::\8
         *  Reserved for IPX Allocation                   02xx::\8
         *  Aggregatable Global Unicast Addresses         2xxx::\3           (全球单播)
         *  Link-Local Unicast Addresses                  fe80::\10          (本地链路地址)
         *  Site-Local Unicast Addresses                  feC0::\10          (本地站点地址)
         *  Multicast Addresses                           FFxx::\8           (广播地址)
         *  
         * 
         *    单播地址     subnet(n)    +   interface id(128-n)
         *    泛播地址     subnet(n)    +   itnerface id(all 0) (128-n)
         *    多播地址     FF (8bits)   +   flag(4bits)   +  scope(4bits)  + groupID(112bits)
         *                 flag  :0 公认多播地址
         *                       :1 多播地址(自定)
         *                 scope : 1    （节点内有效)-->只发给下一跳的节点或路由
         *                       : 2     (链路内有效)-->发给同一个链路内的所有节点或路由
         *                       : 5     (站点有效,网络有效)   --->发给站内所有的点 或路由
         *                       : 8     (组织内有效)
         *                       : E     (全局有效)
         *                       
         *    例如 :            所有节点的地址：
         *                      FF01:0:0:0:0:0:0:1 （节点本地）
         *                      FF02:0:0:0:0:0:0:1 （链路本地）
         *                      所有路由器:
         *                      FF01:0:0:0:0:0:0:2 （节点本地） 
         *                      FF02:0:0:0:0:0:0:2 （链路本地） 
         *                      FF05:0:0:0:0:0:0:2 （站点本地）
         *                      被请求节点的地址  :
         *                      FF02:0:0:0:0:1:FFXX:XXXX 后24位为interface (或EUI64后24位)
         */



        class BitControl
        {
            byte[] _mbytes;

            public BitControl(byte[] from) { _mbytes = (byte[])from.Clone(); }
            public BitControl(int size)
            { _mbytes = new byte[size]; }

            public void BitSet(int offset, byte value)
            {
                int o = offset % 8;
                if (value > 0)
                {
                    _mbytes[offset / 8] |= (byte)(1 << (7 - o));
                }
                else
                {
                    _mbytes[offset / 8] &= (byte)(~((byte)(1 << (7 - o))));
                }
            }

            public byte BitGet(int offset)
            {
                byte c = _mbytes[offset / 8];
                offset %= 8;
                if ((byte)((c >> (7 - offset)) & 0x01) > 0) return 1;
                else return 0;
            }

            public byte[] toByteArray() { return _mbytes; }
        }





        /// <summary>
        /// mac ---->64bit address (interface id)
        /// </summary>
        /// <param name="sub"></param>
        /// <returns></returns>
        public static IPAddress ToEUI48(byte[] sub)
        {
            byte[] ip = new byte[8];
            Array.Copy(sub, 0, ip, 0, 3);
            Array.Copy(sub, 3, ip, 5, 3);
            ip[0] |= 0x02;
            ip[3] = 0xff;
            ip[4] = 0xfe;
            return ToEUI64(ip);
        }

        public static IPAddress ToEUI48(byte[] sub, int offset) {
            byte[] d = new byte[6];
            Array.Copy(sub, offset, d, 0, 6);
            return ToEUI48(d);
        }

        public static byte[] FromEUI64 (byte[] sub,int offset){
            byte[] mac = new byte[6];
            Array.Copy(sub, offset + 2, mac, 0, 6);
            mac[0] |= 0x02;
            mac[0] &= 0xFE;
            return mac;
        }

        /// <summary>
        /// sub[8] ----->EUI64 address
        /// </summary>
        /// <param name="sub"></param>
        /// <returns></returns>
        public static IPAddress ToEUI64(byte[] sub)
        {
            if (sub == null || sub.Length < 8) return null;

            String addr = ":";

            for (int i = 0; i < 4; i++)
            {
                addr += ":" + BitConverter.ToString(sub, 2 * i, 2).Replace("-", "");
            }

            IPAddress ip = IPv6IPAddressWithPrefix("0000::", 64, IPAddress.Parse(addr));
            byte[] s = ip.GetAddressBytes();

            //第一个字节的 是 xxxxxx10
            s[8] |= 0x02;
            s[8] &= 0xFE;

            addr = "";
            for (int j = 0; j < 7; j++)
            {
                addr += BitConverter.ToString(s, 2 * j, 2).Replace("-", "") + ":";
            }
            addr += BitConverter.ToString(s, 14, 2).Replace("-", "");

            return IPAddress.Parse(addr);
        }


        /// <summary>
        ///  产生一个unicast ipaddress 
        /// </summary>
        /// <param name="prefix"></param>
        /// <param name="ip"></param>
        /// <returns> prefix + ip</returns>
        public static IPAddress IPv6IPAddressWithPrefix(String prefix, int prefixlen, IPAddress ip)
        {

            try
            {
                BitControl p = new BitControl(IPAddress.Parse(prefix).GetAddressBytes());
                BitControl ss = new BitControl(ip.GetAddressBytes());
                for (int i = 0; i < prefixlen; i++)
                {
                    ss.BitSet(i, p.BitGet(i));
                }


                String addr = "";
                byte[] s = ss.toByteArray();
                for (int j = 0; j < 7; j++)
                {
                    addr += BitConverter.ToString(s, 2 * j, 2).Replace("-", "") + ":";
                }
                addr += BitConverter.ToString(s, 14, 2).Replace("-", "");
                return IPAddress.Parse(addr);
            }
            catch { return null; }
        }

        #region 构造
        public IPv6Address(byte[]address):base(address){ }
        public IPv6Address(long newAddress) : base(newAddress) { }
        public IPv6Address(byte[] address, long scopeId) : base(address, scopeId) { }
        #endregion


        public static String AddressToString(IPAddress address) {
            return AddressToString(address.GetAddressBytes());
        }

        public static String AddressToString(byte[] address) {
            String r = "";
            byte[] b = address;
            for (int i = 0; i < 7; i++)
            {
                r += BitConverter.ToString(b, i * 2, 2).Replace("-", "") + ":";
            }
            r += BitConverter.ToString(b, 14, 2).Replace("-", "");
            return r;
            
        }

        public static String AddressToString(byte[] address, int offset) {
            try
            {
                byte[] a = new byte[16];
                Array.Copy(address, offset, a, 0, 16);
                return AddressToString(a);
            }
            catch { return null; }

        }
    
    }
}
